package org.jsets.fastboot.persistent.interceptor;

import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.Collection;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.jsets.fastboot.persistent.config.PersistentContext;
import org.jsets.fastboot.persistent.metadata.FieldBindOption;
import org.jsets.fastboot.persistent.metadata.FieldDesensitizeOption;
import org.jsets.fastboot.persistent.metadata.FieldEncryptOption;
import org.jsets.fastboot.persistent.metadata.PersistentOption;
import org.jsets.fastboot.util.ReflectUtils;
import org.jsets.fastboot.util.SpringContextHolder;
import org.jsets.fastboot.persistent.config.PersistentAnnotationResolver;
import org.jsets.fastboot.persistent.IDesensitizeService;
import org.jsets.fastboot.persistent.IEncryptService;
import org.jsets.fastboot.persistent.IFieldBindService;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 
 * ResultSetHandler 拦截器
 * 
 * @author wangjie
 *
 */
@Intercepts({ 
	@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) 
})
public class ResultSetHandlerInterceptor implements Interceptor {

	@Autowired
	private IEncryptService encryptService;

	@Autowired
	private IDesensitizeService desensitizeService;

	private IFieldBindService fieldBindService;

	@Override
	@SuppressWarnings("all")
	public Object intercept(Invocation invocation) throws Throwable {
		Object result = invocation.proceed();
		if (result instanceof Collection) {
			boolean cleanupNecessary = false;
			for (Object item : (Collection) result) {
				if(null == item) {
					continue;
				}
				if (!PersistentAnnotationResolver.includeOption(item.getClass())) {
					break;
				}
				PersistentOption option = PersistentAnnotationResolver.getOption(item.getClass());
				this.handle(item, option);
				cleanupNecessary = true;
			}
			if(cleanupNecessary) {
				PersistentContext.cleanup();// 清空PersistentContext
			}
		} else {
			if (PersistentAnnotationResolver.includeOption(result.getClass())) {
				PersistentOption option = PersistentAnnotationResolver.getOption(result.getClass());
				this.handle(result, option);
				PersistentContext.cleanup();// 清空PersistentContext
			}
		}

		return result;
	}

	public void handle(Object entity, PersistentOption option) {
		// 绑定
		if (option.isBindEnable()) {
			for (FieldBindOption fbo : option.getFieldBinds()) {
				Method readMethod = ReflectUtils.getReadMethod(entity.getClass(), fbo.getBindField(), true);
				Object bindValue = ReflectUtils.invokeMethod(entity, readMethod);
				if (null == bindValue) {
					continue;
				}
				Object targetValue = this.getFieldBindService().getTargetValue(fbo.getBindType(), bindValue);
				Method writeMethod = ReflectUtils.getWriteMethod(entity.getClass(), fbo.getTargetField(), true);
				ReflectUtils.invokeMethod(entity, writeMethod, targetValue);
			}
		}

		// 加密&&解密
		if (option.isEncryptEnable()) {
			for (FieldEncryptOption eo : option.getFieldEncrypts()) {
				if (eo.isEncryptOnQuery()) {
					if (eo.isLazy() && !PersistentContext.isEncryptNecessary()) {
						continue;
					}
					Method readMethod = ReflectUtils.getReadMethod(entity.getClass(), eo.getField(), true);
					Object value = ReflectUtils.invokeMethod(entity, readMethod);
					if (null == value) {
						continue;
					}
					String plaintext = (String) value;
					Object ciphertext = this.getEncryptService().encrypt(eo.getAlgorithm(), plaintext);
					Method writeMethod = ReflectUtils.getWriteMethod(entity.getClass(), eo.getField(), true);
					ReflectUtils.invokeMethod(entity, writeMethod, ciphertext);
				}

				if (eo.isDecryptOnQuery()) {
					if (eo.isLazy() && !PersistentContext.isDecryptNecessary()) {
						continue;
					}
					Method readMethod = ReflectUtils.getReadMethod(entity.getClass(), eo.getField(), true);
					Object value = ReflectUtils.invokeMethod(entity, readMethod);
					if (null == value) {
						continue;
					}
					String ciphertext = (String) value;
					Object newValue = this.getEncryptService().decrypt(eo.getAlgorithm(), ciphertext);
					Method writeMethod = ReflectUtils.getWriteMethod(entity.getClass(), eo.getField(), true);
					ReflectUtils.invokeMethod(entity, writeMethod, newValue);
				}
			}
		}

		// 脱敏
		if (option.isDesensitizeEnable()) {
			for (FieldDesensitizeOption so : option.getFieldDesensitizeOptions()) {
				if (so.isLazy() && !PersistentContext.isDesensitizeNecessary()) {
					continue;
				}
				Method readMethod = ReflectUtils.getReadMethod(entity.getClass(), so.getField(), true);
				Object value = ReflectUtils.invokeMethod(entity, readMethod);
				if (null == value) {
					continue;
				}
				String plaintext = (String) value;
				Object desensitized = this.getDesensitizeService().desensitized(so.getFormat(), plaintext);
				Method writeMethod = ReflectUtils.getWriteMethod(entity.getClass(), so.getField(), true);
				ReflectUtils.invokeMethod(entity, writeMethod, desensitized);
			}
		}
	}

	private IFieldBindService getFieldBindService() {
		if (null != this.fieldBindService) {
			return this.fieldBindService;
		}

		synchronized (this) {
			if (null != this.fieldBindService) {
				return this.fieldBindService;
			}
			this.fieldBindService = SpringContextHolder.getBean(IFieldBindService.class);
			return this.fieldBindService;
		}
	}

	private IEncryptService getEncryptService() {
		return this.encryptService;
	}

	private IDesensitizeService getDesensitizeService() {
		return this.desensitizeService;
	}

}